/*
 * @Copyright: Citylife 2021
 * @Copyright: EasyCompany 2021
 * @Author: asterisk
 * @Date: 2021-03-15 15:43:42
 * @Description: 
 */

import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';

import '../easy_log.dart';
import 'git.dart';
import 'storage/dao.dart';
import 'storage/file.dart';

/// Log object create factory class
/// You can create a singleton log object that is held by the factory.
/// Multiple instance objects can be created, and the objects are held by the caller.
class EasyLoggerFactory {
  static EasyLoggerFactory _instance;

  /// app name
  /// Affect [head()] result
  /// Affect [EasyLogDBAppender] result
  static String appName = EasyLogConst.appDefaultName;

  /// app version
  /// Affect [EasyLogger][head] result
  static List<int> appVersion = EasyLogConst.appDefaultVersion;

  /// git commit id
  /// .git folder : .git/HEAD
  /// .git/refs/heads/master
  static String gitTag = '';

  /// app date
  /// Affect [EasyLogger][head] result
  static DateTime appDate = EasyLogConst.appDefaultDate;

  /// Log file saved directory
  /// All log record save a file.
  static String appLogSaveDir;

  /// file volume type
  static EasyLogFileVolumeType appFileVolumeType = EasyLogFileVolumeType.appVolume;

  /// global log parameters
  static EasyLogParams logParams = EasyLogParams();

  /// for test
  static bool isTestMode = false;

  /// env
  static String env = EasyLogEnvType.dev;

  /// default logger object
  EasyLogger _logger;
  // Map<String, EasyLogger> _namedLoggers = {};

  /// constructor
  EasyLoggerFactory._();

  /// get single instance
  static EasyLoggerFactory get instance {
    _instance ??= EasyLoggerFactory._();
    return _instance;
  }

  /// init logger object
  /// ```
  /// @param appName app name
  /// @param appVersion app version
  /// @param appDate App date
  /// @param logSaveDir saved directory
  /// @param logLevel log filter level
  /// @param supportConsole is or not console output
  /// @param supportDao is or not database output
  /// @param supportFile is or not file output
  /// @param env reference [EasyLogEnvType]
  /// @param projectDirectory project directory for git tag
  /// @param volumeType file volume type
  /// ```
  Future init(
      {String appName,
      List<int> appVersion,
      DateTime appDate,
      String logSaveDir,
      EasyLogLevel logLevel,
      bool supportConsole,
      bool supportDao,
      bool supportFile,
      String env,
      Directory projectDirectory,
      EasyLogFileVolumeType fileVolumeType}) async {
    if (null != appName) {
      EasyLoggerFactory.appName = appName;
    }
    if (null != appVersion) {
      EasyLoggerFactory.appVersion = appVersion;
    }
    if (null != appDate) {
      EasyLoggerFactory.appDate = appDate;
    }
    if (null != env) {
      EasyLoggerFactory.env = env;
    }
    gitTag = GitInfoHelper.getGitTag(currentDirectory: projectDirectory);
    EasyLoggerFactory.gitTag =
        gitTag.length > 8 ? gitTag.substring(0, 8) : gitTag;
    if (null != logSaveDir) {
      EasyLoggerFactory.appLogSaveDir = logSaveDir;
    } else {
      if (true == EasyLoggerFactory.isTestMode) {
        EasyLoggerFactory.appLogSaveDir = './test';
      } else {
        EasyLoggerFactory.appLogSaveDir = await _getLogFileDirectory();
      }
    }
    if (null != supportConsole) {
      logParams.supportConsole = supportConsole;
    }
    if (kIsWeb) {
      logParams.supportDao = false;
      logParams.supportFile = false;
    } else {
      if (null != supportDao) {
        logParams.supportDao = supportDao;
      }
      if (null != supportFile) {
        logParams.supportFile = supportFile;
      }
    }

    if (true == logParams.supportDao) {
      await createDao().open(EasyLoggerFactory.appLogSaveDir,
          EasyLoggerFactory.appName, EasyLoggerFactory.env);
    }
    if (true == logParams.supportFile) {
      createFile().openSync(EasyLoggerFactory.appLogSaveDir,
          EasyLoggerFactory.appName, EasyLoggerFactory.env);
    }

    List<Future> list = [];
    logger().config.appenders.forEach((appender) async {
      list.add(appender.init());
    });
    await Future.wait(list);
  }

  Future unInit() async {
    if (null != _logger) {
      List<Future> list = [];
      _logger.config.appenders.forEach((appender) async {
        list.add(appender.unInit());
      });
      await Future.wait(list);
      _logger.dispose();
      _logger = null;
    }
  }

  /// Get logger object
  /// ```
  /// @param logName logger object name
  /// @param logSaveDir saved directory
  /// @param logLevel log filter record level
  /// @param supportConsole is or not console output
  /// @param supportDao is or not database output
  /// ```
  EasyLogger logger(
      {String logName = '',
      String logSaveDir,
      EasyLogLevel logLevel,
      bool supportConsole,
      bool supportDao}) {
    if (logName.isEmpty) {
      if (null == _logger) {
        var config = _createConfig(EasyLogParams(
            isInstance: true,
            logLevel: logLevel ??= EasyLoggerFactory.logParams.logLevel,
            logName: EasyLoggerFactory.appName,
            supportConsole: supportConsole ??=
                EasyLoggerFactory.logParams.supportConsole,
            supportDao: supportDao ??= EasyLoggerFactory.logParams.supportDao));
        _logger = EasyLogger(config: config);
        if (isTestMode) {
          _logger.head();
        }
      }
      return _logger;
    } else {
      var config = _createConfig(EasyLogParams(
          isInstance: false,
          logLevel: logLevel ??= EasyLoggerFactory.logParams.logLevel,
          logName: logName,
          supportConsole: supportConsole ??=
              EasyLoggerFactory.logParams.supportConsole,
          supportDao: supportDao ??= EasyLoggerFactory.logParams.supportDao));
      var logger = EasyLogger(config: config);
      if (isTestMode) {
        _logger.head();
      }
      return logger;
    }
  }

  EasyLoggerConfig _createConfig(EasyLogParams params) {
    var ret =
        EasyLoggerConfig(logName: params.logName, logLevel: params.logLevel);
    ret.addFormatter(EasyLogInfoFormatter(ret));
    ret.addFormatter(EasyLogMessageFormatter(ret));
    if (params.supportConsole) {
      ret.addAppender(EasyLogConsoleAppender(ret));
    }
    if (params.supportDao) {
      ret.addAppender(EasyLogDBAppender(ret));
    }
    return ret;
  }

  /// example : .../easyshop/
  Future<String> _getLogFileDirectory() async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path + '/' + EasyLoggerFactory.appName;
  }

  /// get log file name
  Future<String> _getLogFileName() async {}
}
